﻿/*
 *FileName:      LoopScroll.cs
 *Author:        AUTHOR
 *Date:          2023/09/11 17:43:26
 *UnityVersion:  2021.3.15f1c1
 *Description:
*/
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System;
using UnityEngine.EventSystems;
public class LoopScroll : MonoBehaviour, IEndDragHandler
{
    public enum LoopScrollType
    {
        Vertical,
        Horizontal
    }
    public enum RefreshType
    {
        All,
        Top,
        Bottom,
        Left,
        Right
    }
    public LoopScrollType scrollAxis = LoopScrollType.Vertical;
    public GameObject content;
    public GameObject itemPrefab;
    public Vector2 spacingV2;
    public int spacing;
    public int paddingTop;
    public int paddingBottom;
    public int paddingLeft;
    public int paddingRight;
    public int activeCountCol; //列
    public int activeCountRow; //行
    public Vector2 itemSize;
    public bool isAutoLayout = false;
    private int appliedLoad = 2;
    public ScrollRect scroll;
    public LoopScrollItem[] items;
    private Action arriveBottomEvent;
    private Action arriveTopEvent;
    private RectTransform scrollRect;
    private RectTransform contenRect;
    private CanvasGroup canvasGroup;
    private IList itemDatas;
    private int dataStart;
    private int dataEnd;
    private int startShowDataIndex;
    public float moveDis;
    private Vector3 lastPos;
    private bool isReady = false;
    public void Refresh(IList _datas, int _startShowDataIndex = 0)
    {
        if (_datas == null || _datas.Count == 0)
        {
            gameObject.SetActive(false);
            return;
        }
        gameObject.SetActive(true);
        startShowDataIndex = _startShowDataIndex;
        itemDatas = _datas;
        if (!isReady)
        {
            InitOnce();
        }
        for (int i = 0; i < items.Length; i++)
        {
            items[i].Clear();
        }
        switch (scrollAxis)
        {
            case LoopScrollType.Vertical:
                {
                    var sizeY = spacingV2.y * (Mathf.Ceil(itemDatas.Count * 1f / activeCountCol) - 1) + itemSize.y + paddingTop + paddingBottom;
                    contenRect.sizeDelta = new Vector2(scrollRect.sizeDelta.x, sizeY);
                    if (contenRect.anchorMin.x == 0 && contenRect.anchorMin.y == 1 && contenRect.anchorMax.x == 1 && contenRect.anchorMax.y == 1)
                    {
                        contenRect.sizeDelta = new Vector2(0, sizeY);
                    }
                    if (startShowDataIndex != -1)
                    {
                        dataStart = -(appliedLoad * activeCountCol) + startShowDataIndex;
                        dataEnd = (activeCountRow * activeCountCol) - 1 + startShowDataIndex;
                        content.transform.localPosition = new Vector3(0, Mathf.Floor(startShowDataIndex / activeCountCol) * spacingV2.y);
                    }
                    scroll.movementType = (contenRect.rect.height >= scrollRect.rect.height) ? ScrollRect.MovementType.Elastic : scroll.movementType = ScrollRect.MovementType.Clamped;
                    lastPos = content.transform.localPosition;
                    RefreshActiveItem();
                    break;
                }
            case LoopScrollType.Horizontal:
                {
                    var sizeX = spacing * (itemDatas.Count - 1) + itemSize.x + paddingLeft + paddingRight;
                    contenRect.sizeDelta = new Vector2(sizeX, scrollRect.sizeDelta.y);
                    if (startShowDataIndex != -1)
                    {
                        dataStart = -appliedLoad + startShowDataIndex;
                        dataEnd = activeCountCol - 1 + startShowDataIndex;
                        content.transform.localPosition = new Vector3(-startShowDataIndex * spacing, 0);
                    }
                    scroll.movementType = (contenRect.rect.width >= scrollRect.rect.width) ? ScrollRect.MovementType.Elastic : scroll.movementType = ScrollRect.MovementType.Clamped;
                    lastPos = content.transform.localPosition;
                    RefreshActiveItem();
                    break;
                }
        }

    }

    public void SetArriveBottomEvent(Action _arriveBottomEvent)
    {
        arriveBottomEvent = _arriveBottomEvent;
    }
    public void SetArriveTopEvent(Action _arriveTopEvent)
    {
        arriveTopEvent = _arriveTopEvent;
    }
    public void ShowScroll()
    {
        if (canvasGroup == null)
        {
            canvasGroup = gameObject.AddComponent<CanvasGroup>();
        }
        canvasGroup.alpha = 1;
        canvasGroup.interactable = true;
        canvasGroup.blocksRaycasts = true;
    }
    public void HideScroll()
    {
        if (canvasGroup == null)
        {
            canvasGroup = gameObject.AddComponent<CanvasGroup>();
        }
        canvasGroup.alpha = 0;
        canvasGroup.interactable = false;
        canvasGroup.blocksRaycasts = false;
    }
    private void InitOnce()
    {
        if (isReady) return;
        isReady = true;
        scrollRect = transform.GetComponent<RectTransform>();
        contenRect = content.transform.GetComponent<RectTransform>();
        RefreshAllItemPrefabs();
        lastPos = content.transform.localPosition;
        scroll.onValueChanged.AddListener(ScrollEndEvent);
    }
    public void RefreshAllItemPrefabs()
    {
        if (content == null) return;
        if (itemPrefab == null) return;
        switch (scrollAxis)
        {
            case LoopScrollType.Vertical:
                {
                    if (isAutoLayout)
                    {
                        AutoFill();
                    }
                    dataStart = -(appliedLoad * activeCountCol);
                    dataEnd = (activeCountRow * activeCountCol) - 1;
                    int totalActiveItemCount = (appliedLoad + activeCountRow) * activeCountCol;
                    int clildCount = content.transform.childCount - 1;
                    if (activeCountRow != 0 && activeCountCol != 0)
                    {
                        for (int i = 0; i < clildCount; i++)
                        {
                            GameObject.DestroyImmediate(content.transform.GetChild(1).gameObject);
                        }
                        itemPrefab.SetActive(false);
                        items = new LoopScrollItem[totalActiveItemCount];
                        var itemNodesIndex = 0;
                        for (int i = dataStart; i <= dataEnd; i++)
                        {
                            var obj = GameObject.Instantiate(itemPrefab);
                            obj.transform.SetParent(content.transform, false);
                            obj.SetActive(true);
                            obj.name = obj.name + i;
                            //obj.transform.localPosition = GetPositionByDataIndex(i);
                            var temp = obj.GetComponent<LoopScrollItem>();
                            if (temp == null)
                            {
                                Debug.LogError("item未挂载脚本");
                            }
                            temp.InitOnce(itemPrefab.name);
                            items[itemNodesIndex] = temp;
                            itemNodesIndex++;
                        }
                    }
                    break;
                }
            case LoopScrollType.Horizontal:
                {
                    dataStart = -appliedLoad;
                    dataEnd = activeCountCol - 1;
                    int totalActiveItemCount = appliedLoad + activeCountCol;
                    int clildCount = content.transform.childCount - 1;
                    if (activeCountCol != 0)
                    {
                        for (int i = 0; i < clildCount; i++)
                        {
                            GameObject.DestroyImmediate(content.transform.GetChild(1).gameObject);
                        }
                        itemPrefab.SetActive(false);
                        items = new LoopScrollItem[totalActiveItemCount];
                        var itemNodesIndex = 0;
                        for (int i = dataStart; i <= dataEnd; i++)
                        {
                            var obj = GameObject.Instantiate(itemPrefab);
                            obj.transform.SetParent(content.transform, false);
                            obj.SetActive(true);
                            obj.name = obj.name + i;
                            //obj.transform.localPosition = GetPositionByDataIndex(i);
                            var temp = obj.GetComponent<LoopScrollItem>();
                            if (temp == null)
                            {
                                Debug.LogError("item未挂载脚本");
                            }
                            temp.InitOnce(itemPrefab.name);
                            items[itemNodesIndex] = temp;
                            itemNodesIndex++;
                        }
                    }
                    break;
                }
        }
    }

    private void RefreshActiveItem(RefreshType _refreshType = RefreshType.All)
    {
        int tempDataStart = 0;
        int tempDataEnd = 0;
        int tempIndex = 0;
        switch (_refreshType)
        {
            case RefreshType.All:
                tempDataStart = dataStart;
                tempDataEnd = dataEnd;
                tempIndex = 0;
                break;
            case RefreshType.Top:
                tempDataStart = dataStart;
                tempDataEnd = dataStart + activeCountCol - 1;
                tempIndex = items.Length - activeCountCol;
                break;
            case RefreshType.Bottom:
                tempDataStart = dataEnd - (activeCountCol - 1);
                tempDataEnd = dataEnd;
                tempIndex = 0;
                break;
            case RefreshType.Left:
                tempDataStart = dataStart;
                tempDataEnd = dataStart;
                tempIndex = items.Length - 1;
                break;
            case RefreshType.Right:
                tempDataStart = dataEnd;
                tempDataEnd = dataEnd;
                tempIndex = 0;
                break;
        }
        for (int i = tempDataStart; i <= tempDataEnd; i++)
        {
            if (i >= 0 && i < itemDatas.Count)
            {
                items[tempIndex].DataInit(i, this.itemDatas[i]);
                items[tempIndex].transform.localPosition = this.GetPositionByDataIndex(i);
            }
            else if (i >= this.itemDatas.Count)
            {
                items[tempIndex].DataInit(i, null);
            }
            else
            {
                items[tempIndex].DataInit(i, null);
            }
            tempIndex++;
        }
    }
    private void ScrollEndEvent(Vector2 v2)
    {
        switch (scrollAxis)
        {
            case LoopScrollType.Vertical:
                moveDis += content.transform.localPosition.y - lastPos.y;
                lastPos = content.transform.localPosition;
                if (moveDis > spacingV2.y)
                {
                    if (moveDis > spacingV2.y)
                    {
                        moveDis -= spacingV2.y;
                        ChangeItemTopToBottom();
                    }
                }
                else if (moveDis < -spacingV2.y)
                {
                    if (moveDis < -spacingV2.y)
                    {
                        moveDis += spacingV2.y;
                        ChangeItemBottomToTop();
                    }
                }
                break;
            case LoopScrollType.Horizontal:
                moveDis += content.transform.localPosition.x - lastPos.x;
                lastPos = content.transform.localPosition;
                if (moveDis > spacing)
                {
                    if (moveDis > spacing)
                    {
                        moveDis -= spacing;
                        ChangeItemRightToLeft();
                    }
                }
                else if (moveDis < -spacing)
                {
                    if (moveDis < -spacing)
                    {
                        moveDis += spacing;
                        ChangeItemLeftToRight();
                    }
                }
                break;
            default:
                break;
        }
    }
    private Vector3 GetPositionByDataIndex(int _index)
    {
        if (_index < 0)
        {
            return new Vector3(2000, 2000);
        }
        switch (scrollAxis)
        {
            case LoopScrollType.Vertical:
                {
                    Vector3 resPos = new Vector3(0, 0, 0);
                    float offset = (activeCountCol % 2 == 0) ? (activeCountCol / 2 - 0.5f) * spacingV2.x : Mathf.Floor(activeCountCol / 2) * spacingV2.x;
                    float resPosX = _index % activeCountCol * spacingV2.x - offset;
                    float resPosY = -(Mathf.Floor(_index / activeCountCol) * spacingV2.y + (itemSize.y / 2) + paddingTop);
                    resPos = new Vector3(resPosX, resPosY, 0);
                    return resPos;
                }
            case LoopScrollType.Horizontal:
                {
                    Vector3 resPos = new Vector3(0, 0, 0);
                    float resPosX = _index * spacing + paddingLeft + (0.5f * itemSize.x);
                    resPos = new Vector3(resPosX, 0);
                    return resPos;
                }
        }
        return new Vector3(2000, 2000);
    }
    private void ChangeItemLeftToRight()
    {
        dataStart += 1;
        dataEnd += 1;
        RefreshActiveItem(RefreshType.Right);
        ChangeItemsIndexLeftToRight();
    }
    private void ChangeItemRightToLeft()
    {
        dataStart -= 1;
        dataEnd -= 1;
        RefreshActiveItem(RefreshType.Left);
        ChangeItemsIndexRightToLeft();

    }
    private void ChangeItemBottomToTop()
    {
        dataStart -= activeCountCol;
        dataEnd -= activeCountCol;
        RefreshActiveItem(RefreshType.Top);
        ChangeItemsIndexBottomToTop();
    }
    private void ChangeItemTopToBottom()
    {
        dataStart += activeCountCol;
        dataEnd += activeCountCol;
        RefreshActiveItem(RefreshType.Bottom);
        ChangeItemsIndexTopToBottom();
    }
    private void ChangeItemsIndexRightToLeft()
    {
        LoopScrollItem[] temp = new LoopScrollItem[items.Length];
        for (int i = 0; i < items.Length; i++)
        {
            int tempIndex = (i < items.Length - 1) ? 1 + i : i - (items.Length - 1);
            temp[tempIndex] = items[i];
        }
        items = temp;
    }
    private void ChangeItemsIndexLeftToRight()
    {
        LoopScrollItem[] temp = new LoopScrollItem[items.Length];
        for (int i = 0; i < items.Length; i++)
        {
            int tempIndex = (i < 1) ? items.Length - 1 + i : i - 1;
            temp[tempIndex] = items[i];
        }
        items = temp;

    }
    private void ChangeItemsIndexTopToBottom()
    {
        LoopScrollItem[] temp = new LoopScrollItem[items.Length];
        for (int i = 0; i < items.Length; i++)
        {
            int tempIndex = (i < activeCountCol) ? items.Length - activeCountCol + i : i - activeCountCol;
            temp[tempIndex] = items[i];
        }
        items = temp;
    }
    private void ChangeItemsIndexBottomToTop()
    {
        LoopScrollItem[] temp = new LoopScrollItem[items.Length];
        for (int i = 0; i < items.Length; i++)
        {
            int tempIndex = (i < items.Length - activeCountCol) ? activeCountCol + i : i - (items.Length - activeCountCol);
            temp[tempIndex] = items[i];
        }
        items = temp;
    }
    private void AutoFill()
    {
        scrollRect = transform.GetComponent<RectTransform>();
        float width = scrollRect.rect.width - paddingLeft - paddingRight;
        float height = scrollRect.rect.height - paddingTop - paddingBottom;
        activeCountCol = (int)(width / itemSize.x);
        spacingV2.x = ((width % itemSize.x) / (activeCountCol - 1)) + itemSize.x;
        spacingV2.y = ((width % itemSize.x) / (activeCountCol - 1)) + itemSize.y;
        activeCountRow = (int)(height / spacingV2.y) + 3;
    }
    public void GetItemSize()
    {
        var rect = itemPrefab.GetComponent<RectTransform>().rect;
        var width = rect.width;
        var height = rect.height;
        itemSize = new Vector2(width, height);
    }
    public void OnEndDrag(PointerEventData eventData)
    {
        if (contenRect.localPosition.y >= contenRect.rect.height - scrollRect.rect.height && contenRect.rect.height >= scrollRect.rect.height)
        {
            arriveBottomEvent?.Invoke();
        }
        if (contenRect.localPosition.y < 0)
        {
            arriveTopEvent?.Invoke();
        }
    }

    public void Preview()
    {
        if (content == null) return;
        if (itemPrefab == null) return;
        switch (scrollAxis)
        {
            case LoopScrollType.Vertical:
                {
                    if (isAutoLayout)
                    {
                        AutoFill();
                    }
                    dataStart = -(appliedLoad * activeCountCol);
                    dataEnd = (activeCountRow * activeCountCol) - 1;
                    int clildCount = content.transform.childCount - 1;
                    if (activeCountRow != 0 && activeCountCol != 0)
                    {
                        for (int i = 0; i < clildCount; i++)
                        {
                            GameObject.DestroyImmediate(content.transform.GetChild(1).gameObject);
                        }
                        itemPrefab.SetActive(false);
                        for (int i = dataStart; i <= dataEnd; i++)
                        {
                            var obj = GameObject.Instantiate(itemPrefab);
                            obj.transform.SetParent(content.transform, false);
                            obj.SetActive(true);
                            obj.name = obj.name + i;
                            obj.transform.localPosition = GetPositionByDataIndex(i);
                            var temp = obj.GetComponent<LoopScrollItem>();
                            if (temp == null)
                            {
                                Debug.LogError("item未挂载脚本");
                            }
                        }
                    }
                    break;
                }
            case LoopScrollType.Horizontal:
                {
                    dataStart = -appliedLoad;
                    dataEnd = activeCountCol - 1;
                    int clildCount = content.transform.childCount - 1;
                    if (activeCountCol != 0)
                    {
                        for (int i = 0; i < clildCount; i++)
                        {
                            GameObject.DestroyImmediate(content.transform.GetChild(1).gameObject);
                        }
                        itemPrefab.SetActive(false);
                        for (int i = dataStart; i <= dataEnd; i++)
                        {
                            var obj = GameObject.Instantiate(itemPrefab);
                            obj.transform.SetParent(content.transform, false);
                            obj.SetActive(true);
                            obj.name = obj.name + i;
                            obj.transform.localPosition = GetPositionByDataIndex(i);
                            var temp = obj.GetComponent<LoopScrollItem>();
                            if (temp == null)
                            {
                                Debug.LogError("item未挂载脚本");
                            }
                        }
                    }
                    break;
                }
        }
    }
    public void CancelPreview()
    {
        int clildCount = content.transform.childCount - 1;
        for (int i = 0; i < clildCount; i++)
        {
            GameObject.DestroyImmediate(content.transform.GetChild(1).gameObject);
        }
    }
}

